
#include <BGridLayout>
#include <BVBoxLayout>
#include <BHBoxLayout>

#include "EdgeMergeBoard.h"

EdgeMergeBoard::EdgeMergeBoard(BWidget* parent) : BDialog(parent)
{
	this->setTitle("Edge Merge");

	BGridLayout* gridLayout = new BGridLayout();
	{
		_originVertexNumberLabel = new BLabel();
		_newVertexNumberLabel = new BLabel();
		_minEdgeLengthLabel = new BLabel();
		_distanceLimitSpinBox = new BRealSpinBox();
		_distanceLimitSpinBox->setWheelEnabled(true);
		_distanceLimitSpinBox->setArrowVisible(true);
		_distanceLimitSpinBox->setDecimals(10);
		_refreshButton = new BButton("refresh");
		_refreshButton->setCheckable(true);
		_informationLabel = new BLabel();

		gridLayout->addWidget(0, 0, new BLabel("origin vertex number : "));
		gridLayout->addWidget(0, 1, _originVertexNumberLabel);
		gridLayout->addWidget(1, 0, new BLabel("new vertex number : "));
		gridLayout->addWidget(1, 1, _newVertexNumberLabel);
		gridLayout->addWidget(2, 0, new BLabel("min edge length : "));
		gridLayout->addWidget(2, 1, _minEdgeLengthLabel);
		gridLayout->addWidget(3, 0, new BLabel("distance limit : "));
		gridLayout->addWidget(3, 1, _distanceLimitSpinBox);
		gridLayout->addWidget(3, 2, _refreshButton);
		gridLayout->addWidget(4, 0, new BLabel("information : "));
		gridLayout->addWidget(4, 1, _informationLabel);
		gridLayout->setColumnStretch(1, 100);
	}

	_progressBar = new BProgressBar();

	BVBoxLayout* vlayout = new BVBoxLayout(this);
	vlayout->addLayout(gridLayout);
	vlayout->addWidget(_progressBar);

	BHBoxLayout* bottomLayout = new BHBoxLayout(this, Part_Bottom);
	{
		_applyButton = new BButton("apply");
		_cancelButton = new BButton("cancel");
		bottomLayout->addSpring();
		bottomLayout->addWidget(_applyButton);
		bottomLayout->addWidget(_cancelButton);
	}

	connect(this, Signal_AssetChanged, &EdgeMergeBoard::slotAssetChanged);
	connect(_refreshButton, Signal_Toggled, &EdgeMergeBoard::slotRefreshButtonToggled);
	connect(_distanceLimitSpinBox, Signal_EditFinished, &EdgeMergeBoard::slotDistanceLimitEditFinished);
	connect(_applyButton, Signal_Clicked, &EdgeMergeBoard::slotApplyButtonClicked);
	connect(_cancelButton, Signal_Clicked, &EdgeMergeBoard::slotCancelButtonClicked);
}
EdgeMergeBoard::~EdgeMergeBoard()
{

}

int EdgeMergeBoard::closestIndex(int index, BReal limit)
{
	const BVector& point = _vertices->at(index);
	for (int i = 0; i < _newVertices->size(); i++)
	{
		const BVector& vec = _newVertices->at(i);
		if ((point - vec).length() < limit)
		{
			return i;
		}
	}
	int size = _newVertices->size();
	_newVertices->append(point);
	_newNormals->append(_normals->at(index));
	return size;
}

void EdgeMergeBoard::slotAssetChanged(BObject* object, const BValue& value)
{
	BMesh* mesh = value;
	if (mesh)
	{
		_vertices = mesh->vertices();
		if (_vertices)
		{
			mesh->setVertices(new BVectorArray(*_vertices));
			_originVertexNumberLabel->setText(_vertices->size());
			_newVertexNumberLabel->setText(_vertices->size());
		}
		_normals = mesh->normals();
		if (_normals)
		{
			mesh->setNormals(new BVectorArray(*_normals));
		}
		BColorArray* colors = mesh->colors();
		if (colors)
		{
			mesh->setColors(new BColorArray(*colors));
		}
		BCoordArray* texCoords = mesh->texCoords();
		if (texCoords)
		{
			mesh->setTexCoords(new BCoordArray(*texCoords));
		}
		BReal minlen = REAL_MAX;
		BFaceArray* faces = new BFaceArray(_vertices->size() / 3);
		for (int i = 0; i < _vertices->size(); i += 3)
		{
			const BVector& va = _vertices->at(i);
			const BVector& vb = _vertices->at(i + 1);
			const BVector& vc = _vertices->at(i + 2);
			BReal len_ab = (va - vb).length();
			BReal len_bc = (vb - vc).length();
			BReal len_ca = (vc - va).length();
			if (minlen > len_ab)
				minlen = len_ab;
			if (minlen > len_bc)
				minlen = len_bc;
			if (minlen > len_ca)
				minlen = len_ca;
			faces->append(BFace(i, i + 1, i + 2));
		}
		mesh->setFaces(faces);
		_minEdgeLengthLabel->setText(minlen);
		_distanceLimitSpinBox->setRange(0, minlen / 2);
		_distanceLimitSpinBox->setStep(minlen / 20);
	}
}

void EdgeMergeBoard::slotDistanceLimitEditFinished(BObject* object, const BValue& value)
{
	BReal limit = value;
	_refreshButton->setEnabled(limit > 0);
}
void EdgeMergeBoard::slotRefreshButtonToggled(BObject* object, const BValue& value)
{
	if (BMesh* mesh = this->asset())
	{
		_currentIndex = 0;
		BFaceArray* faces = mesh->faces();
		faces->reset();
		_newVertices = mesh->vertices();
		_newVertices->reset();
		_newNormals = mesh->normals();
		_newNormals->reset();
		mesh->dirty();
		_newVertexNumberLabel->setText(_currentIndex);
		_progressBar->setRange(0, _vertices->size());
		_progressBar->setValue(_currentIndex);
		_applyButton->setEnabled(true);
	}
	else
	{
		_newVertices = 0;
		_newNormals = 0;
	}
}
void EdgeMergeBoard::slotApplyButtonClicked(BObject* object, const BValue& value)
{
	this->close(true);
}
void EdgeMergeBoard::slotCancelButtonClicked(BObject* object, const BValue& value)
{
	if (BMesh* mesh = this->asset())
	{
		mesh->setVertices(_vertices);
		mesh->setNormals(_normals);
		mesh->setColors(_colors);
		mesh->setTexCoords(_texCoords);
		mesh->setFaces(0);
		mesh->dirty();
	}
	this->close();
}

void EdgeMergeBoard::showEvent(const BEvent& event)
{
	BDialog::showEvent(event);
}
void EdgeMergeBoard::hideEvent(const BEvent& event)
{
	BDialog::hideEvent(event);
	if (BShape* shape = this->asset())
	{
		_vertices = 0;
		_normals = 0;
		this->setAsset(BValue());
	}
}
void EdgeMergeBoard::updateEvent(const BEvent& event)
{
	if (_refreshButton->checked())
	{
		if (BMesh* mesh = this->asset())
		{
			BReal beginTime = event.time();
			if (_currentIndex < _vertices->size())
			{
				BReal limit = _distanceLimitSpinBox->value();
				BFaceArray* faces = mesh->faces();
				while (_currentIndex < _vertices->size())
				{
					int ia = closestIndex(_currentIndex + 0, limit);
					int ib = closestIndex(_currentIndex + 1, limit);
					int ic = closestIndex(_currentIndex + 2, limit);
					faces->append(BFace(ia, ib, ic));
					_currentIndex += 3;
					if (event.now() - beginTime > 0.1)
					{
						break;
					}
				}
				mesh->dirty();
				_newVertexNumberLabel->setText(_newVertices->size());
				_progressBar->setValue(_currentIndex);
				if (_currentIndex == _vertices->size())
				{
					_refreshButton->setChecked(false);
					_newVertices = 0;
					_newNormals = 0;
					mesh->smooth();
				}
			}
		}
	}
}
